package com.g0dkar.leet.util.common;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class StringUtils {
	/**
	 * Provides a faster way to access {@link System#getProperty(String) System.getProperty("line.separator")}. It is the host OS line separator string ("\r\n" at Windows, "\n" at Unices)
	 */
	public static final String LINE_SEPARATOR = System.getProperty("line.separator");
	
	private StringUtils() throws InstantiationException {
		throw new InstantiationException("You cannot create an instance of this class.");
	}

	/**
	 * Checks if a given {@link String} is null or an empty string.
	 * 
	 * @param string
	 *            The String
	 * @return <b>true</b> if <code>(string == null || string.length() == 0)</code>
	 */
	public static boolean isEmpty(String string) {
		return string == null || string.length() == 0;
	}
	
	/**
	 * Checks if a given string {@link #isEmpty(String) is empty} or is simply "blank space".
	 * 
	 * @param string
	 *            The String
	 * @return <b>true</b> if <code>isEmpty(string) || string.matches("\\s*")</code>
	 */
	public static boolean isBlank(String string) {
		return isEmpty(string) || string.matches("\\s*");
	}
	
	/**
	 * Same as {@link #isEmpty(String)}, but for {@link StringBuilder}.
	 * 
	 * @param string
	 *            The {@link StringBuilder}
	 * @return <b>true</b> if <code>(string == null || string.length() == 0)</code>
	 * @see #isEmpty(String)
	 */
	public static boolean isEmpty(StringBuilder string) {
		return string == null || string.length() == 0;
	}
	
	/**
	 * Same as {@link #isEmpty(String)}, but for {@link StringBuffer}.
	 * 
	 * @param string
	 *            The {@link StringBuffer}
	 * @return <b>true</b> if <code>(string == null || string.length() == 0)</code>
	 * @see #isEmpty(String)
	 */
	public static boolean isEmpty(StringBuffer string) {
		return string == null || string.length() == 0;
	}
	
	/**
	 * Returns a CammelCaseClassName as a phrase: "Cammel Case Class Name".
	 * 
	 * @param className
	 *            The CammelCaseClassName
	 * @return The "phrase" - Cammel Case Class Name
	 */
	public static String classnameAsPhrase(String className) {
		StringBuilder builder = new StringBuilder();
		
		Matcher matcher = Pattern.compile("\\p{Upper}\\p{Lower}*").matcher(className);
		while (matcher.find()) {
			builder.append(className.substring(matcher.start(), matcher.end()) + " ");
		}
		
		return builder.toString().trim();
	}
	
	/**
	 * Return a String as a ClassName
	 * 
	 * @param name
	 *            The String
	 * @return The String as a ClassName
	 * @see #asMethod(String)
	 */
	public static String asClass(String name) {
		return name.substring(0, 1).toUpperCase() + name.substring(1);
	}
	
	/**
	 * Return a String as a methodName
	 * 
	 * @param name
	 *            The String
	 * @return The String as a methodName
	 * @see #asClass(String)
	 */
	public static String asMethod(String name) {
		return name.substring(0, 1).toLowerCase() + name.substring(1);
	}
	
	/**
	 * Return a String as a getMethodName
	 * 
	 * @param name
	 *            The String
	 * @return The String as a getMethodName
	 * @see #asMethod(String)
	 */
	public static String asGetMethod(String name) {
		return "get" + asClass(name);
	}
	
	/**
	 * Return a String as a isMethodName (boolean get method name convention)
	 * 
	 * @param name
	 *            The String
	 * @return The String as a isMethodName (boolean get method name convention)
	 * @see #asMethod(String)
	 */
	public static String asIsMethod(String name) {
		return "is" + asClass(name);
	}
	
	/**
	 * Return a String as a setMethodName
	 * 
	 * @param name
	 *            The String
	 * @return The String as a setMethodName
	 * @see #asMethod(String)
	 */
	public static String asSetMethod(String name) {
		return "set" + asClass(name);
	}
	
	/**
	 * Retorna o nome de um campo caso a string informada seja um getter (get/is) ou um setter (set).
	 * Caso contrário, returna null.
	 * 
	 * @param acessorName
	 * @return
	 */
	public static String asField(String acessorName) {
		if (acessorName.startsWith("get") || acessorName.startsWith("set")) {
			return asMethod(acessorName.substring(3));
		} else if (acessorName.startsWith("is")) {
			return asMethod(acessorName.substring(2));
		}
		return null;
	}
	
	/**
	 * <p>
	 * Separa uma string em partes separadas por uma {@link Matcher expressão regular}. Por exemplo:
	 * </p>
	 * 
	 * <p>
	 * string = <code>"elemento1, elemento2, elemento3"</code>
	 * </p>
	 * 
	 * <p>
	 * separatorRegEx = <code>"\\s*,\\s*"</code>
	 * </p>
	 * 
	 * <p>
	 * Resultado = <code>ArrayList -> [ "elemento1", "elemento2", "elemento3" ]</code>
	 * </p>
	 * 
	 * <p>
	 * Uma chamada a este método retorna o mesmo que <code>Arrays.asList(string.split(separatorRegEx))</code>.
	 * </p>
	 * 
	 * @param string
	 *            String a ser separada
	 * @param separatorRegEx
	 *            Expressão regular do separador
	 * @return Lista de strings.
	 * 
	 * @see String#split(String)
	 * @see Matcher
	 */
	public static List<String> split(String string, String separatorRegEx) {
		return Arrays.asList(string.split(separatorRegEx));
	}
	
	/**
	 * <p>
	 * Separa uma string em partes separadas por uma outra string. <strong>Não utiliza expressões regulares e sim
	 * {@link String#indexOf(String)}</strong>. Por exemplo:
	 * </p>
	 * 
	 * <p>
	 * string = <code>"elemento1, elemento2, elemento3"</code>
	 * </p>
	 * 
	 * <p>
	 * separatorRegEx = <code>", "</code>
	 * </p>
	 * 
	 * <p>
	 * Resultado = <code>ArrayList -> [ "elemento1", "elemento2", "elemento3" ]</code>
	 * </p>
	 * 
	 * @param string
	 *            String a ser separada
	 * @param separatorRegEx
	 *            Expressão regular do separador
	 * @return Lista de strings.
	 */
	public static List<String> splitNormal(String string, String separator) {
		List<String> list = new ArrayList<String>();
		
		int lastIndex = 0;
		while (string.indexOf(separator, lastIndex) >= 0) {
			list.add(string.substring(lastIndex, string.indexOf(separator, lastIndex)));
			lastIndex = string.indexOf(separator, lastIndex);
		}
		
		return list;
	}
	
	/**
	 * <p>
	 * Concatena uma coleção de strings em uma só, utilizando outra string como <em>joiner</em>.
	 * </p>
	 * 
	 * <p>
	 * strings = <code>ArrayList -> [ "elemento1", "elemento2", "elemento3" ]</code>
	 * </p>
	 * 
	 * <p>
	 * joinString = <code>","</code>
	 * </p>
	 * 
	 * <p>
	 * Resultado = <code>"elemento1,elemento2,elemento3"</code>
	 * </p>
	 * 
	 * @param strings
	 *            Coleção de Strings
	 * @param joinString
	 *            String <em>Joiner</em>
	 * @return String concatenada.
	 */
	public static String join(Collection<String> strings, String joinString) {
		StringBuilder str = new StringBuilder();
		if (joinString == null){
			joinString = "";
		}
		
		if (strings != null) {
			Iterator<String> iterator = strings.iterator();
			while (iterator.hasNext()) {
				str.append(iterator.next());
				if (iterator.hasNext()) {
					str.append(joinString);
				}
			}
		}
		
		return str.toString();
	}
	
	/**
	 * Joins the Strings in <b>strings</b> array into one String, with individual Strings separated by
	 * <b>joinString</b>.
	 * 
	 * @see #join(Collection, String)
	 * 
	 * @param strings
	 * @param joinString
	 * @return
	 */
	public static String join(String[] strings, String joinString) {
		if (strings == null) {
			return null;
		}
		
		return join(Arrays.asList(strings), joinString);
	}
	
	/**
	 * Converts the first character of given string to upper case.
	 * 
	 * "ice cream" -> "Ice cream"
	 * 
	 * @param str
	 *            The String
	 * @return The Capitalized string
	 */
	public static String capitalize(String str) {
		if (isBlank(str)) {
			return str;
		}
		else
			if (str.length() == 1) {
				return str.toUpperCase();
			}
			else {
				return str.substring(0, 1).toUpperCase() + str.substring(1);
			}
	}
	
	/**
	 * Converts the enumeration constant to a sensible default.
	 * 
	 * "ICE_CREAM" -> "Ice cream"
	 * 
	 * @param str
	 * @return
	 */
	public static String enumConstantAsPhrase(String enumValue) {
		return isEmpty(enumValue) ? enumValue : capitalize(enumValue.replaceAll("_", " ").toLowerCase());
	}
	
	/**
	 * Performs a {@link String#replaceAll(String, String) replaceAll}-like operation on a {@link StringBuilder}. It
	 * DOES NOT uses RegEx. It does as "hard replace" - replaces all occurrences of the search text with the replace
	 * text.
	 * 
	 * @param string
	 *            The {@link StringBuilder}
	 * @param replaceThis
	 *            The Search String
	 * @param withThis
	 *            The Replace String
	 */
	public static void replaceAll(StringBuilder string, String replaceThis, String withThis) {
		int startIndex = 0;
		int endIndex;
		int replaceThisLen = replaceThis.length();
		int withThisLen = withThis.length();
		
		while ((startIndex = string.indexOf(replaceThis, startIndex)) > 0) {
			endIndex = startIndex + replaceThisLen;
			string.replace(startIndex, endIndex, withThis);
			startIndex = startIndex + withThisLen;
		}
	}
}
